#!/usr/bin/env python3
"""
ArXiv Subscription Platform - Main API Server
Integration coordinator for all 6 development agent components

This FastAPI application serves as the central coordination layer that:
- Provides REST endpoints for frontend communication
- Manages database connections and ORM
- Orchestrates services (scraper, AI classification, email)
- Handles authentication middleware
- Provides unified error handling and logging
"""

import asyncio
import logging
import os
import sys
from contextlib import asynccontextmanager
from typing import Dict, List, Optional

import uvicorn
from fastapi import FastAPI, HTTPException, Depends, BackgroundTasks, Request
from fastapi.middleware.cors import CORSMiddleware
from fastapi.middleware.trustedhost import TrustedHostMiddleware
from fastapi.responses import JSONResponse
from pydantic import BaseModel, EmailStr
import psycopg2
from psycopg2.extras import RealDictCursor
import json

# Import our service modules
sys.path.append(os.path.dirname(os.path.abspath(__file__)))

try:
    from arxiv_scraper_service.core.scraper_service import ArxivScraperService
    from arxiv_scraper_service.core.config_manager import ConfigManager as ScraperConfigManager
    from ai_classification_engine.core.classification_engine import AIClassificationEngine
    from ai_classification_engine.core.recommendation_engine import PersonalizedRecommendationEngine
    scraper_available = True
    ai_available = True
except ImportError as e:
    logging.warning(f"Some services not available: {e}")
    scraper_available = False
    ai_available = False

# Configuration
class AppConfig:
    """Application configuration"""
    def __init__(self):
        self.database_url = os.getenv("DATABASE_URL", "postgresql://arxiv_user:password@localhost:5432/arxiv_platform")
        self.supabase_url = os.getenv("SUPABASE_URL", "")
        self.supabase_key = os.getenv("SUPABASE_ANON_KEY", "")
        self.jwt_secret = os.getenv("JWT_SECRET", "your-jwt-secret")
        self.environment = os.getenv("ENVIRONMENT", "development")
        self.log_level = os.getenv("LOG_LEVEL", "INFO")
        self.allowed_hosts = os.getenv("ALLOWED_HOSTS", "localhost,127.0.0.1").split(",")
        self.cors_origins = os.getenv("CORS_ORIGINS", "http://localhost:3000,http://localhost:5173").split(",")

config = AppConfig()

# Logging setup
logging.basicConfig(
    level=getattr(logging, config.log_level),
    format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
logger = logging.getLogger(__name__)

# Global service instances
db_connection = None
scraper_service = None
ai_engine = None
recommendation_engine = None

# Database connection management
async def get_db_connection():
    """Get database connection"""
    global db_connection
    if not db_connection or db_connection.closed:
        try:
            db_connection = psycopg2.connect(
                config.database_url,
                cursor_factory=RealDictCursor
            )
        except Exception as e:
            logger.error(f"Database connection failed: {e}")
            raise HTTPException(status_code=500, detail="Database connection failed")
    return db_connection

# Service initialization
async def initialize_services():
    """Initialize all backend services"""
    global scraper_service, ai_engine, recommendation_engine
    
    try:
        # Initialize database connection
        await get_db_connection()
        logger.info("Database connection established")
        
        # Initialize ArXiv scraper service
        if scraper_available:
            scraper_config_manager = ScraperConfigManager('arxiv_scraper_service/config.yaml')
            scraper_service = ArxivScraperService(scraper_config_manager.get_config())
            logger.info("ArXiv scraper service initialized")
        
        # Initialize AI classification engine
        if ai_available:
            ai_config = {
                "database": {
                    "connection_string": config.database_url
                },
                "embedding": {
                    "model_name": "all-MiniLM-L6-v2",
                    "use_gpu": False,
                    "batch_size": 32
                }
            }
            ai_engine = AIClassificationEngine(ai_config)
            recommendation_engine = PersonalizedRecommendationEngine(ai_config)
            logger.info("AI classification engine initialized")
            
    except Exception as e:
        logger.error(f"Service initialization failed: {e}")
        # Continue without failing services for graceful degradation

# Application lifecycle
@asynccontextmanager
async def lifespan(app: FastAPI):
    """Application startup and shutdown"""
    # Startup
    logger.info("Starting ArXiv Subscription Platform API")
    await initialize_services()
    
    yield
    
    # Shutdown
    logger.info("Shutting down ArXiv Subscription Platform API")
    if db_connection and not db_connection.closed:
        db_connection.close()

# FastAPI app creation
app = FastAPI(
    title="ArXiv Subscription Platform API",
    description="Integrated API server coordinating scraper, AI classification, and user services",
    version="1.0.0",
    lifespan=lifespan
)

# Middleware
app.add_middleware(
    CORSMiddleware,
    allow_origins=config.cors_origins,
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)

app.add_middleware(
    TrustedHostMiddleware,
    allowed_hosts=config.allowed_hosts
)

# Request models
class UserRegistration(BaseModel):
    email: EmailStr
    password: str
    full_name: str
    display_name: Optional[str] = None
    gdpr_consent: bool = True
    marketing_consent: bool = False

class UserLogin(BaseModel):
    email: EmailStr
    password: str
    remember_me: bool = False

class PaperPreferences(BaseModel):
    categories: List[str] = []
    keywords: List[str] = []
    authors: List[str] = []
    institutions: List[str] = []

class FeedbackData(BaseModel):
    user_id: str
    paper_id: str
    feedback_type: str  # 'like', 'bookmark', 'view', 'download'
    feedback_data: Dict = {}

# Root endpoint
@app.get("/")
async def root():
    """API root endpoint"""
    return {
        "message": "ArXiv Subscription Platform API",
        "version": "1.0.0",
        "status": "online",
        "services": {
            "scraper": scraper_available,
            "ai_classification": ai_available,
            "database": db_connection is not None
        }
    }

# Health check endpoint
@app.get("/health")
async def health_check():
    """Health check endpoint for monitoring"""
    health_status = {
        "status": "healthy",
        "timestamp": asyncio.get_event_loop().time(),
        "services": {}
    }
    
    # Check database
    try:
        conn = await get_db_connection()
        with conn.cursor() as cur:
            cur.execute("SELECT 1")
            health_status["services"]["database"] = "healthy"
    except Exception as e:
        health_status["services"]["database"] = f"unhealthy: {str(e)}"
        health_status["status"] = "degraded"
    
    # Check scraper service
    if scraper_available and scraper_service:
        health_status["services"]["scraper"] = "healthy"
    else:
        health_status["services"]["scraper"] = "unavailable"
    
    # Check AI service
    if ai_available and ai_engine:
        health_status["services"]["ai_classification"] = "healthy"
    else:
        health_status["services"]["ai_classification"] = "unavailable"
    
    return health_status

# Authentication endpoints
@app.post("/api/auth/register")
async def register_user(user: UserRegistration, background_tasks: BackgroundTasks):
    """Register a new user"""
    try:
        conn = await get_db_connection()
        with conn.cursor() as cur:
            # Check if user already exists
            cur.execute("SELECT id FROM user_profiles WHERE email = %s", (user.email,))
            if cur.fetchone():
                raise HTTPException(status_code=400, detail="User already exists")
            
            # Create user profile (simplified - in production, use Supabase auth)
            cur.execute("""
                INSERT INTO user_profiles (email, full_name, display_name, gdpr_consent)
                VALUES (%s, %s, %s, %s)
                RETURNING id, email, full_name, display_name
            """, (user.email, user.full_name, user.display_name, user.gdpr_consent))
            
            new_user = cur.fetchone()
            conn.commit()
            
            # Add background task for welcome email
            background_tasks.add_task(send_welcome_email, user.email, user.full_name)
            
            return {
                "success": True,
                "message": "Registration successful",
                "user": dict(new_user)
            }
            
    except Exception as e:
        logger.error(f"Registration failed: {e}")
        raise HTTPException(status_code=500, detail="Registration failed")

@app.post("/api/auth/login")
async def login_user(user: UserLogin):
    """Authenticate user (simplified implementation)"""
    try:
        conn = await get_db_connection()
        with conn.cursor() as cur:
            cur.execute("""
                SELECT id, email, full_name, display_name, last_login
                FROM user_profiles 
                WHERE email = %s AND is_active = true
            """, (user.email,))
            
            user_data = cur.fetchone()
            if not user_data:
                raise HTTPException(status_code=401, detail="Invalid credentials")
            
            # Update last login
            cur.execute("""
                UPDATE user_profiles 
                SET last_login = NOW() 
                WHERE id = %s
            """, (user_data['id'],))
            conn.commit()
            
            return {
                "success": True,
                "message": "Login successful",
                "user": dict(user_data),
                "token": "jwt-token-here"  # In production, generate actual JWT
            }
            
    except HTTPException:
        raise
    except Exception as e:
        logger.error(f"Login failed: {e}")
        raise HTTPException(status_code=500, detail="Login failed")

# User management endpoints
@app.get("/api/users/{user_id}/profile")
async def get_user_profile(user_id: str):
    """Get user profile"""
    try:
        conn = await get_db_connection()
        with conn.cursor() as cur:
            cur.execute("""
                SELECT id, email, full_name, display_name, paper_preferences, 
                       notification_preferences, subscription_status, created_at
                FROM user_profiles 
                WHERE id = %s
            """, (user_id,))
            
            profile = cur.fetchone()
            if not profile:
                raise HTTPException(status_code=404, detail="User not found")
            
            return dict(profile)
            
    except HTTPException:
        raise
    except Exception as e:
        logger.error(f"Get profile failed: {e}")
        raise HTTPException(status_code=500, detail="Failed to get profile")

@app.put("/api/users/{user_id}/preferences/papers")
async def update_paper_preferences(user_id: str, preferences: PaperPreferences):
    """Update user paper preferences"""
    try:
        conn = await get_db_connection()
        with conn.cursor() as cur:
            preferences_json = json.dumps({
                "categories": preferences.categories,
                "keywords": preferences.keywords,
                "authors": preferences.authors,
                "institutions": preferences.institutions
            })
            
            cur.execute("""
                UPDATE user_profiles 
                SET paper_preferences = %s, updated_at = NOW()
                WHERE id = %s
            """, (preferences_json, user_id))
            
            if cur.rowcount == 0:
                raise HTTPException(status_code=404, detail="User not found")
            
            conn.commit()
            
            return {"success": True, "message": "Preferences updated"}
            
    except HTTPException:
        raise
    except Exception as e:
        logger.error(f"Update preferences failed: {e}")
        raise HTTPException(status_code=500, detail="Failed to update preferences")

# Paper and recommendation endpoints
@app.get("/api/recommendations/{user_id}")
async def get_recommendations(user_id: str, count: int = 20):
    """Get personalized paper recommendations"""
    if not ai_available or not recommendation_engine:
        raise HTTPException(status_code=503, detail="AI service unavailable")
    
    try:
        recommendations = recommendation_engine.get_recommendations(user_id, count)
        return {
            "success": True,
            "recommendations": recommendations,
            "count": len(recommendations)
        }
    except Exception as e:
        logger.error(f"Get recommendations failed: {e}")
        raise HTTPException(status_code=500, detail="Failed to get recommendations")

@app.post("/api/feedback")
async def process_feedback(feedback: FeedbackData):
    """Process user feedback for recommendation learning"""
    if not ai_available or not recommendation_engine:
        raise HTTPException(status_code=503, detail="AI service unavailable")
    
    try:
        result = recommendation_engine.update_recommendations_from_feedback(
            feedback.user_id, 
            feedback.paper_id, 
            feedback.feedback_type, 
            feedback.feedback_data
        )
        return {"success": True, "result": result}
    except Exception as e:
        logger.error(f"Process feedback failed: {e}")
        raise HTTPException(status_code=500, detail="Failed to process feedback")

# Scraper management endpoints
@app.post("/api/scraper/run")
async def run_scraper(background_tasks: BackgroundTasks):
    """Trigger ArXiv scraper run"""
    if not scraper_available or not scraper_service:
        raise HTTPException(status_code=503, detail="Scraper service unavailable")
    
    try:
        # Run scraper in background
        background_tasks.add_task(run_scraper_task)
        return {"success": True, "message": "Scraper started"}
    except Exception as e:
        logger.error(f"Scraper run failed: {e}")
        raise HTTPException(status_code=500, detail="Failed to start scraper")

@app.get("/api/scraper/status")
async def get_scraper_status():
    """Get scraper service status"""
    if not scraper_available:
        return {"status": "unavailable", "message": "Scraper service not loaded"}
    
    try:
        # Get scraper metrics/status
        return {
            "status": "available",
            "service": "ready"
        }
    except Exception as e:
        logger.error(f"Get scraper status failed: {e}")
        raise HTTPException(status_code=500, detail="Failed to get scraper status")

# Background tasks
async def send_welcome_email(email: str, full_name: str):
    """Send welcome email (placeholder)"""
    logger.info(f"Sending welcome email to {email}")
    # In production, integrate with email service
    pass

async def run_scraper_task():
    """Run scraper task in background"""
    try:
        if scraper_service:
            results = scraper_service.run_incremental_scrape()
            logger.info(f"Scraper completed: {results}")
            
            # Trigger AI classification for new papers
            if ai_available and ai_engine:
                # Get newly scraped papers and classify them
                pass
                
    except Exception as e:
        logger.error(f"Scraper task failed: {e}")

# Error handlers
@app.exception_handler(HTTPException)
async def http_exception_handler(request: Request, exc: HTTPException):
    return JSONResponse(
        status_code=exc.status_code,
        content={"detail": exc.detail, "status_code": exc.status_code}
    )

@app.exception_handler(Exception)
async def general_exception_handler(request: Request, exc: Exception):
    logger.error(f"Unhandled exception: {exc}")
    return JSONResponse(
        status_code=500,
        content={"detail": "Internal server error", "status_code": 500}
    )

if __name__ == "__main__":
    port = int(os.getenv("PORT", 8000))
    host = os.getenv("HOST", "0.0.0.0")
    
    logger.info(f"Starting server on {host}:{port}")
    uvicorn.run(
        "main:app",
        host=host,
        port=port,
        reload=config.environment == "development",
        log_level=config.log_level.lower()
    )